/**
  ******************************************************************************
  * @file    usbd_core.c
  * @author  MCD Application Team
  * @version V2.2.0
  * @date    13-June-2014
  * @brief   This file provides all the USBD core functions.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2014 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software 
  * distributed under the License is distributed on an "AS IS" BASIS, 
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */ 

/* Includes ------------------------------------------------------------------*/
#include "usbd_core.h"

/** @addtogroup STM32_USBD_DEVICE_LIBRARY
* @{
*/


/** @defgroup USBD_CORE 
* @brief usbd core module
* @{
*/ 

/** @defgroup USBD_CORE_Private_TypesDefinitions
* @{
*/ 
/**
* @}
*/ 


/** @defgroup USBD_CORE_Private_Defines
* @{
*/ 

/**
* @}
*/ 


/** @defgroup USBD_CORE_Private_Macros
* @{
*/ 
/**
* @}
*/ 

/** @defgroup USBD_CORE_Private_FunctionPrototypes
* @{
*/ 

/**
* @}
*/ 

/** @defgroup USBD_CORE_Private_Variables
* @{
*/ 

/**
* @}
*/ 

/** @defgroup USBD_CORE_Private_Functions
* @{
*/ 

/**
* @brief  USBD_Init
*         Initailizes the device stack and load the class driver
* @param  pdev: device instance
* @param  pdesc: Descriptor structure address
* @param  id: Low level core index
* @retval None
*/
USBD_StatusTypeDef USBD_Init(USBD_HandleTypeDef *pdev, USBD_DescriptorsTypeDef *pdesc, uint8_t id)
{
	/* Check whether the USB Host handle is valid */
	if (pdev == NULL)
	{
		USBD_ErrLog("Invalid Device handle");
		return USBD_FAIL; 
	}

	/* Unlink previous class */
	if (pdev->pClass != NULL)
	{
		pdev->pClass = NULL;
	}

	/* Assign USBD Descriptors */
	if (pdesc != NULL)
	{
		pdev->pDesc = pdesc;
	}

	/* Set Device initial State */
	pdev->dev_state  = USBD_STATE_DEFAULT;
	pdev->id = id;
	/* Initialize low level driver */
	USBD_LL_Init(pdev);

	return USBD_OK; 
}

/**
* @brief  USBD_DeInit 
*         Re-Initialize th device library
* @param  pdev: device instance
* @retval status: status
*/
USBD_StatusTypeDef USBD_DeInit(USBD_HandleTypeDef *pdev)
{
	/* Set Default State */
	pdev->dev_state  = USBD_STATE_DEFAULT;

	/* Free Class Resources */
	pdev->pClass->DeInit(pdev, pdev->dev_config);  

	/* Stop the low level driver  */
	USBD_LL_Stop(pdev); 

	/* Initialize low level driver */
	USBD_LL_DeInit(pdev);

	return USBD_OK;
}


/**
  * @brief  USBD_RegisterClass 
  *         Link class driver to Device Core.
  * @param  pDevice : Device Handle
  * @param  pclass: Class handle
  * @retval USBD Status
  */
USBD_StatusTypeDef  USBD_RegisterClass(USBD_HandleTypeDef *pdev, USBD_ClassTypeDef *pclass)
{
	USBD_StatusTypeDef status = USBD_OK;
	if (pclass != 0)
	{
		/* link the class tgo the USB Device handle */
		pdev->pClass = pclass;
		status = USBD_OK;
	}
	else
	{
		USBD_ErrLog("Invalid Class handle");
		status = USBD_FAIL; 
	}

	return status;
}

/**
  * @brief  USBD_Start 
  *         Start the USB Device Core.
  * @param  pdev: Device Handle
  * @retval USBD Status
  */
USBD_StatusTypeDef  USBD_Start  (USBD_HandleTypeDef *pdev)
{
	/* Start the low level driver  */
	USBD_LL_Start(pdev); 

	return USBD_OK;  
}

/**
  * @brief  USBD_Stop 
  *         Stop the USB Device Core.
  * @param  pdev: Device Handle
  * @retval USBD Status
  */
USBD_StatusTypeDef  USBD_Stop   (USBD_HandleTypeDef *pdev)
{
	/* Free Class Resources */
	pdev->pClass->DeInit(pdev, pdev->dev_config);  

	/* Stop the low level driver  */
	USBD_LL_Stop(pdev); 

	return USBD_OK;  
}

/**
* @brief  USBD_RunTestMode 
*         Launch test mode process
* @param  pdev: device instance
* @retval status
*/
USBD_StatusTypeDef  USBD_RunTestMode (USBD_HandleTypeDef  *pdev) 
{
	return USBD_OK;
}


/**
* @brief  USBD_SetClassConfig 
*        Configure device and start the interface
* @param  pdev: device instance
* @param  cfgidx: configuration index
* @retval status
*/

USBD_StatusTypeDef USBD_SetClassConfig(USBD_HandleTypeDef  *pdev, uint8_t cfgidx)
{
	USBD_StatusTypeDef   ret = USBD_FAIL;

	if (pdev->pClass != NULL)
	{
		/* Set configuration  and Start the Class*/
		if (pdev->pClass->Init(pdev, cfgidx) == 0)
		{
			ret = USBD_OK;
		}
	}
	return ret; 
}

/**
* @brief  USBD_ClrClassConfig 
*         Clear current configuration
* @param  pdev: device instance
* @param  cfgidx: configuration index
* @retval status: USBD_StatusTypeDef
*/
USBD_StatusTypeDef USBD_ClrClassConfig(USBD_HandleTypeDef  *pdev, uint8_t cfgidx)
{
	/* Clear configuration  and Deinitialize the Class process*/
	pdev->pClass->DeInit(pdev, cfgidx);  
	return USBD_OK;
}


/**
* @brief  USBD_SetupStage 
*         Handle the setup stage
* @param  pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_SetupStage(USBD_HandleTypeDef *pdev, uint8_t *psetup)
{
	USBD_ParseSetupRequest(&pdev->request, psetup);

	pdev->ep0_state = USBD_EP0_SETUP;
	pdev->ep0_data_len = pdev->request.wLength;

	switch (pdev->request.bmRequest & 0x1F) 
	{
	case USB_REQ_RECIPIENT_DEVICE:   
		USBD_StdDevReq (pdev, &pdev->request);
		break;

	case USB_REQ_RECIPIENT_INTERFACE:     
		USBD_StdItfReq(pdev, &pdev->request);
		break;

	case USB_REQ_RECIPIENT_ENDPOINT:        
		USBD_StdEPReq(pdev, &pdev->request);   
		break;

	default:           
		USBD_LL_StallEP(pdev , pdev->request.bmRequest & 0x80);
		break;
	}  
	return USBD_OK;  
}

/**
* @brief  USBD_DataOutStage 
*         Handle data OUT stage
* @param  pdev: device instance
* @param  epnum: endpoint index
* @retval status
*/
USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev , uint8_t epnum, uint8_t *pdata)
{
	USBD_EndpointTypeDef    *pep;

	if (epnum == 0) 
	{
		pep = &pdev->ep_out[0];

		if ( pdev->ep0_state == USBD_EP0_DATA_OUT)
		{
			if (pep->rem_length > pep->maxpacket)
			{
				pep->rem_length -=  pep->maxpacket;

				USBD_CtlContinueRx (pdev, 
									pdata,
									MIN(pep->rem_length ,pep->maxpacket));
			}
			else
			{
				if ((pdev->pClass->EP0_RxReady != NULL)
				&&	(pdev->dev_state == USBD_STATE_CONFIGURED)
					)
				{
					pdev->pClass->EP0_RxReady(pdev); 
				}
				USBD_CtlSendStatus(pdev);
			}
		}
	}
	else if ((pdev->pClass->DataOut != NULL)
		&&	(pdev->dev_state == USBD_STATE_CONFIGURED)
			)
	{
		pdev->pClass->DataOut(pdev, epnum); 
	}  
	return USBD_OK;
}

/**
* @brief  USBD_DataInStage 
*         Handle data in stage
* @param  pdev: device instance
* @param  epnum: endpoint index
* @retval status
*/
USBD_StatusTypeDef USBD_LL_DataInStage(USBD_HandleTypeDef *pdev ,uint8_t epnum, uint8_t *pdata)
{
	USBD_EndpointTypeDef    *pep;

	if (epnum == 0) 
	{
		pep = &pdev->ep_in[0];

		if ( pdev->ep0_state == USBD_EP0_DATA_IN)
		{
			if (pep->rem_length > pep->maxpacket)
			{
				pep->rem_length -=  pep->maxpacket;
				USBD_CtlContinueSendData(	pdev,
											pdata, 
											pep->rem_length);
			}
			else
			{ /* last packet is MPS multiple, so send ZLP packet */
				if ((pep->total_length % pep->maxpacket == 0)
				&&	(pep->total_length >= pep->maxpacket)
				&&	(pep->total_length < pdev->ep0_data_len )
					)
				{
					USBD_CtlContinueSendData(pdev , NULL, 0);
					pdev->ep0_data_len = 0;
				}
				else
				{
					if ((pdev->pClass->EP0_TxSent != NULL)
					&&	(pdev->dev_state == USBD_STATE_CONFIGURED)
						)
					{
						pdev->pClass->EP0_TxSent(pdev); 
					}          
					USBD_CtlReceiveStatus(pdev);
				}
			}
		}
		if (pdev->dev_test_mode == 1)
		{
			USBD_RunTestMode(pdev); 
			pdev->dev_test_mode = 0;
		}
	}
	else if ((pdev->pClass->DataIn != NULL)
		&&	(pdev->dev_state == USBD_STATE_CONFIGURED)
			)
	{
		pdev->pClass->DataIn(pdev, epnum); 
	}  
	return USBD_OK;
}

/**
* @brief  USBD_LL_Reset 
*         Handle Reset event
* @param  pdev: device instance
* @retval status
*/

USBD_StatusTypeDef USBD_LL_Reset(USBD_HandleTypeDef  *pdev)
{
	/* Open EP0 OUT */
	USBD_LL_OpenEP(	pdev,
					0x00,
					USBD_EP_TYPE_CTRL,
					USB_MAX_EP0_SIZE);

	pdev->ep_out[0].maxpacket = USB_MAX_EP0_SIZE;

	/* Open EP0 IN */
	USBD_LL_OpenEP(	pdev,
					0x80,
					USBD_EP_TYPE_CTRL,
					USB_MAX_EP0_SIZE);

	pdev->ep_in[0].maxpacket = USB_MAX_EP0_SIZE;
	/* Upon Reset call usr call back */
	pdev->dev_state = USBD_STATE_DEFAULT;

	if (pdev->pClassData) 
		pdev->pClass->DeInit(pdev, pdev->dev_config);

	return USBD_OK;
}




/**
* @brief  USBD_LL_Reset 
*         Handle Reset event
* @param  pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_SetSpeed(USBD_HandleTypeDef  *pdev, USBD_SpeedTypeDef speed)
{
  pdev->dev_speed = speed;
  return USBD_OK;
}

/**
* @brief  USBD_Suspend 
*         Handle Suspend event
* @param  pdev: device instance
* @retval status
*/

USBD_StatusTypeDef USBD_LL_Suspend(USBD_HandleTypeDef  *pdev)
{
	pdev->dev_old_state =  pdev->dev_state;
	pdev->dev_state  = USBD_STATE_SUSPENDED;
	return USBD_OK;
}

/**
* @brief  USBD_Resume 
*         Handle Resume event
* @param  pdev: device instance
* @retval status
*/

USBD_StatusTypeDef USBD_LL_Resume(USBD_HandleTypeDef  *pdev)
{
	pdev->dev_state = pdev->dev_old_state;  
	return USBD_OK;
}

/**
* @brief  USBD_SOF 
*         Handle SOF event
* @param  pdev: device instance
* @retval status
*/

USBD_StatusTypeDef USBD_LL_SOF(USBD_HandleTypeDef  *pdev)
{
	if (pdev->dev_state == USBD_STATE_CONFIGURED)
	{
		if (pdev->pClass->SOF != NULL)
		{
			pdev->pClass->SOF(pdev);
		}
	}
	return USBD_OK;
}

/**
* @brief  USBD_IsoINIncomplete 
*         Handle iso in incomplete event
* @param  pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_IsoINIncomplete(USBD_HandleTypeDef  *pdev, uint8_t epnum)
{
	return USBD_OK;
}

/**
* @brief  USBD_IsoOUTIncomplete 
*         Handle iso out incomplete event
* @param  pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_IsoOUTIncomplete(USBD_HandleTypeDef  *pdev, uint8_t epnum)
{
	return USBD_OK;
}

/**
* @brief  USBD_DevConnected 
*         Handle device connection event
* @param  pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_DevConnected(USBD_HandleTypeDef  *pdev)
{
	return USBD_OK;
}

/**
* @brief  USBD_DevDisconnected 
*         Handle device disconnection event
* @param  pdev: device instance
* @retval status
*/
USBD_StatusTypeDef USBD_LL_DevDisconnected(USBD_HandleTypeDef  *pdev)
{
	/* Free Class Resources */
	pdev->dev_state = USBD_STATE_DEFAULT;
	pdev->pClass->DeInit(pdev, pdev->dev_config);
	return USBD_OK;
}
/**
* @}
*/ 


/**
* @}
*/ 


/**
* @}
*/ 

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

